package messaging;

import java.io.*;
import java.net.*;
import java.util.*;
import debug.*;

/**
* Class to handle all socket communication. Since multiple connections will
* exist for each node, they each have to have multiple threads listening for incoming messages.
**/
public class SocketThread extends Thread
{
	private Socket socket;
	private Connection localConnection;
	private Connection remoteConnection;
	private boolean alive;
	private ObjectInputStream sin;
	private ObjectOutputStream sout;
	private MessageHandler mh;
	/**
	* Constructor that builds a thread to listen for incoming messages on this socket
	* @param s The new socket to run on
	*/
	public SocketThread(Socket socket, MessageHandler mh) throws IOException
	{
		this.socket = socket;
		this.mh = mh;
		sout = new ObjectOutputStream(socket.getOutputStream());
		sin = new ObjectInputStream(socket.getInputStream());
		alive = true;
		localConnection = new Connection(socket.getLocalAddress(), socket.getLocalPort());
		remoteConnection = new Connection(socket.getInetAddress(), socket.getPort());
	}
	/**
	* Constantly queries the TCP socket for incoming info, if there is nothing there
	* it blocks
	*/
	public void run()
	{
		Message msg = null;
		while(alive)
		{
			msg = null;
			try
			{
				msg = ( Message )sin.readObject();
			}
			catch(SocketException e)
			{
				//This is normal behavior when a connection is closed.
				Debug.println("Socket Closed");
				//The Socket Closed, kill this connection
				kill();
			}
			catch(EOFException e)
			{
				Debug.println("Connection lost");
				//The Socket Closed, kill this connection
				kill();
			}
			catch(IOException e)
			{
				Debug.println("Error reading a new message: " + e);
			}
			catch(ClassNotFoundException e)
			{
				System.out.println( "Could not load Message Class, make sure Message.class is in this directory");
				System.exit(0);
			}
			catch(Exception e)
			{
				//Some other exception caused this socket to close.
				Debug.println("Caught unhandled exception in SocketThread.");
				kill();
			}
			if(msg != null)
			{
				// a msg arrives to the port
				mh.receive(msg);
			}
			else
			{
				Debug.println("Received a null message.");
			}
		}
		//Try to close the socket.
		close();
	}
	/**
	* Writes a message to the TCP socket.
	* @param msg The outgoing message to place on the socket
	*/
	public synchronized boolean send(Message msg)
	{
		try
		{
			sout.writeObject(msg);
			sout.flush();
		}
		catch(Exception e)
		{
			return false;
		}
		return true;
	}
	
	/**
	* Close down this socket and the listening Thread
	*/
	public void close()
	{
		try
		{
			socket.close();
		}
		catch(Exception e)
		{
		}
	}
	/**
	* Returns whether or not this connection (Node) is still alive.
	* @return true if this node is alive.
	*/
	public boolean isConnectionAlive()
	{
		return alive;
	}
	public void kill()
	{
		Debug.println("Closing a SocketThread.");
		alive = false;
	}
	public Connection getLocalConnection()
	{
		return localConnection;
	}
	public Connection getRemoteConnection()
	{
		return remoteConnection;
	}
	/**
	* @return a string representation of this node.
	*/
	public String toString()
	{
		return socket.toString();
	}
}